/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*

                       S E C U R I T Y   S E R V I C E S

                       H A S H I N G   F U N C T I O N S

               ( U U I D   H A S H I N G   F O R   P R I V A C Y )

GENERAL DESCRIPTION
  This sofware module contains the source code for hashing a fixed
  equipment identifier for privacy protection.

EXTERNAL FUNCTIONS

  sha1_digest() - Creates a SHA1 based hash digest. Originally created for
  conversion of OTP (One-Time Programmable) flash values into the form required
  for UUID. This function has been generalized for the use as a SHA1 hash digest
  for any purpose.
  

Copyright (c) 2006 by QUALCOMM, Incorporated.  All Rights Reserved.
*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/


/*===========================================================================

                        EDIT HISTORY FOR MODULE

  $Header: //source/qcom/qct/platform/winmobile/Src/Kernel/main/latest/OALLib/sha1digest.c#1 $ 
  $DateTime: 2008/03/03 06:31:03 $ $Author: tonyh $

when       who     what, where, why
--------   ---     ----------------------------------------------------------
11/06/07   th      prefast cleanup
07/24/07   ad      Generalized SHA1 UUID hash function for use for any SHA1 hash
                   purpose.
12/18/06   om      Created module.

===========================================================================*/


/*===========================================================================

                     INCLUDE FILES FOR MODULE

===========================================================================*/

#include <windows.h>
#include <oal.h>
#include <bsp.h>
//#include "comdef.h"
#define MIN(x,y)   (((x)>(y))?(y):(x))

/*===========================================================================

            REGIONAL DEFINITIONS AND DECLARATIONS FOR MODULE

===========================================================================*/


/*===========================================================================

            EXTERNAL DEFINITIONS AND DECLARATIONS FOR MODULE

===========================================================================*/

void sha1_digest
(
  UINT8*  equipment_id_ptr,
  UINT16  equipment_id_len,
  UINT8*  uuid_ptr,
  UINT16  uuid_len
);


/*===========================================================================

            LOCAL DEFINITIONS AND DECLARATIONS FOR MODULE

===========================================================================*/

/* This is the state of a running hash */
typedef struct __sechshuuid_ctx_s 
{
	UINT32  counter[2];
	UINT32  iv[16];  
	UINT8   leftover[64];
	UINT16  leftover_size;
}sechshuuid_ctx_s;

/* Forward declarations of the internal working functions */
void sechshuuid_sha1_init
       ( struct __sechshuuid_ctx_s* );
void sechshuuid_sha1_update
       ( struct __sechshuuid_ctx_s*, UINT8*, UINT16*, UINT8*, UINT16 );
void sechshuuid_sha1_final
       ( struct __sechshuuid_ctx_s*, UINT8*, UINT16*, UINT8* );
void sechshuuid_sha1_transform
       ( UINT32*, unsigned char* );

/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
                    EXTERNAL FUNCTION DEFINTIONS
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/

/*===========================================================================

FUNCTION sha1_digest()

DESCRIPTION
  This function will create a unique identifier.

PARAMETERS
  equipment_id_ptr - Pointer to the IMEI / ESN or other fixed ID
  equipment_id_len - Length of above ID in bytes
  uuid_ptr         - Pointer to the pre-allocated space for the UUID
  uuid_len         - Desired length of the UUID in bytes
  
RETURN VALUE
  None.

SIDE EFFECTS
  None.
===========================================================================*/

void sha1_digest
(
  UINT8*  equipment_id_ptr,
  UINT16  equipment_id_len,
  UINT8*  uuid_ptr,
  UINT16  uuid_len
)
{
  struct __sechshuuid_ctx_s  ctx;
  UINT8                      hsh_block[64];
  UINT8                      digest[20];
  int                        i;
  /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/

  /* Sanity check on the parameters */
  if ( (equipment_id_len == 0)
       ||
       (uuid_len == 0) 
       ||
       (equipment_id_len > 64) )
  {
    if ( uuid_ptr ) memset( uuid_ptr, 0, uuid_len );
    return;
  }
  else
  {
    /* Need memory allocated for a full block */
    memcpy( hsh_block, equipment_id_ptr, equipment_id_len );
  }

  /* Initialize the hash */
  memset( &ctx, 0, sizeof( ctx ) );
  sechshuuid_sha1_init( &ctx );

  /* Update the counters (number of bits) */
  if ( (ctx.counter[0] += ((UINT32)equipment_id_len << 3)) 
       <
       ((UINT32)equipment_id_len << 3) )
  {
    (ctx.counter[1])++;
  }
  ctx.counter[1] += ((UINT32) equipment_id_len >> 29);
  
  /* Hash the equipment ID */
  sechshuuid_sha1_final( &ctx,
                         hsh_block,
                         &equipment_id_len,
                         digest );

  for ( i=0; i<=(uuid_len / 20); i++ )
  {
    /* Trim / repeat the result */
    memcpy( uuid_ptr + (i*20), digest, MIN( uuid_len, 20 ) );
  }

} /* sha1_digest() */

/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
                  INTERNAL FUNCTION DEFINTIONS FOR SHA-1
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/

/* ------------------------------------------------------------ */
/* Constants and definitions for SHA-1                          */
/* ------------------------------------------------------------ */
#define SECAPI_HSH_SHA_BLOCK_BYTE_LEN 64

typedef union
{
    unsigned long l[16];
    unsigned char c[64];

} CHAR64LONG16;

/* Optimizations */
unsigned long rol30( unsigned long value)
{
  return (value << 30) | ((value >> 2) & 0x3fffffff);
}

unsigned long rol24( unsigned long value)
{
  return (value << 24) | ((value >> 8) & 0x00ffffff);
}

unsigned long rol8( unsigned long value)
{
  return (value << 8) | ((value >> 24) & 0x000000ff);
}

unsigned long rol5( unsigned long value)
{
  return (value << 5) | ((value >> 27) & 0x0000001f);
}

unsigned long rol1( unsigned long value)
{
  return (value << 1) | ((value >> 31) & 0x00000001);
}

/* blk0() and blk() perform the initial expand */

#define blk0(block, i)      (block->l[i]        = (rol24(block->l[i]) & 0xff00ff00) | (rol8(block->l[i]) & 0x00ff00ff))
#define blk(block, i)       (block->l[i & 0x0f] = rol1(block->l[(i + 13) & 0x0f] ^ block->l[(i +  8) & 0x0f] ^ block->l[(i +  2) & 0x0f] ^ block->l[(i +  0) & 0x0f]))

/* R0, R1, R2, R3, R4 are the different operations used in SHA1 */

#define R0(block, work, v, w, x, y, z, i) \
    (work[z] += ((work[w]&(work[x]^work[y]))^work[y])           + blk0(block, i)    + 0x5a827999 + rol5(work[v]), work[w] = rol30(work[w]))

#define R1(block, work, v, w, x, y, z, i) \
    (work[z] += ((work[w]&(work[x]^work[y]))^work[y])           + blk(block, i)     + 0x5a827999 + rol5(work[v]), work[w] = rol30(work[w]))

#define R2(block, work, v, w, x, y, z, i) \
    (work[z] += (work[w]^work[x]^work[y])                       + blk(block, i)     + 0x6ed9eba1 + rol5(work[v]), work[w] = rol30(work[w]))

#define R3(block, work, v, w, x, y, z, i) \
    (work[z] += (((work[w]|work[x])&work[y])|(work[w]&work[x])) + blk(block, i)     + 0x8f1bbcdc + rol5(work[v]), work[w] = rol30(work[w]))

#define R4(block, work, v, w, x, y, z, i) \
    (work[z] += (work[w]^work[x]^work[y])                       + blk(block, i)     + 0xca62c1d6 + rol5(work[v]), work[w] = rol30(work[w]))

/*===========================================================================

FUNCTION SECHSHUUID_SHA1_TRANSFORM()

DESCRIPTION
  Transform the input block into a digest update.

DEPENDENCIES
  None

RETURN VALUE
  None

SIDE EFFECTS
  None.
===========================================================================*/
void sechshuuid_sha1_transform
(
  UINT32*  iv, 
  unsigned char*  buf
)
{
  unsigned long bufi[16];                             /* internal buffer */
  unsigned long work[5];                      /* internal work registers */
  
  CHAR64LONG16*  block = (CHAR64LONG16*) bufi;
  /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

  /* Copy operating buffer to buf */
  memcpy( bufi, buf, sizeof( bufi ) );
  
  /* Copy context->iv[] to working vars */
  work[0] = iv[0];                 /* a */
  work[1] = iv[1];                 /* b */
  work[2] = iv[2];                 /* c */
  work[3] = iv[3];                 /* d */
  work[4] = iv[4];                 /* e */
  
  /* 4 rounds of 20 operations each. Loop unrolled */
  /* Round 1 */
  R0( block, work, 0, 1, 2, 3, 4,  0 );
  R0( block, work, 4, 0, 1, 2, 3,  1 );
  R0( block, work, 3, 4, 0, 1, 2,  2 );
  R0( block, work, 2, 3, 4, 0, 1,  3 );
  R0( block, work, 1, 2, 3, 4, 0,  4 );
  R0( block, work, 0, 1, 2, 3, 4,  5 );
  R0( block, work, 4, 0, 1, 2, 3,  6 );
  R0( block, work, 3, 4, 0, 1, 2,  7 );
  R0( block, work, 2, 3, 4, 0, 1,  8 );
  R0( block, work, 1, 2, 3, 4, 0,  9 );
  R0( block, work, 0, 1, 2, 3, 4, 10 );
  R0( block, work, 4, 0, 1, 2, 3, 11 );
  R0( block, work, 3, 4, 0, 1, 2, 12 );
  R0( block, work, 2, 3, 4, 0, 1, 13 );
  R0( block, work, 1, 2, 3, 4, 0, 14 );
  R0( block, work, 0, 1, 2, 3, 4, 15 );
  
  R1( block, work, 4, 0, 1, 2, 3, 16 );
  R1( block, work, 3, 4, 0, 1, 2, 17 );
  R1( block, work, 2, 3, 4, 0, 1, 18 );
  R1( block, work, 1, 2, 3, 4, 0, 19 );
  
  /* Round 2 */
  R2( block, work, 0, 1, 2, 3, 4, 20 );
  R2( block, work, 4, 0, 1, 2, 3, 21 );
  R2( block, work, 3, 4, 0, 1, 2, 22 );
  R2( block, work, 2, 3, 4, 0, 1, 23 );
  R2( block, work, 1, 2, 3, 4, 0, 24 );
  R2( block, work, 0, 1, 2, 3, 4, 25 );
  R2( block, work, 4, 0, 1, 2, 3, 26 );
  R2( block, work, 3, 4, 0, 1, 2, 27 );
  R2( block, work, 2, 3, 4, 0, 1, 28 );
  R2( block, work, 1, 2, 3, 4, 0, 29 );
  R2( block, work, 0, 1, 2, 3, 4, 30 );
  R2( block, work, 4, 0, 1, 2, 3, 31 );
  R2( block, work, 3, 4, 0, 1, 2, 32 );
  R2( block, work, 2, 3, 4, 0, 1, 33 );
  R2( block, work, 1, 2, 3, 4, 0, 34 );
  R2( block, work, 0, 1, 2, 3, 4, 35 );
  R2( block, work, 4, 0, 1, 2, 3, 36 );
  R2( block, work, 3, 4, 0, 1, 2, 37 );
  R2( block, work, 2, 3, 4, 0, 1, 38 );
  R2( block, work, 1, 2, 3, 4, 0, 39 );
  
  /* Round 3 */
  R3( block, work, 0, 1, 2, 3, 4, 40 );
  R3( block, work, 4, 0, 1, 2, 3, 41 );
  R3( block, work, 3, 4, 0, 1, 2, 42 );
  R3( block, work, 2, 3, 4, 0, 1, 43 );
  R3( block, work, 1, 2, 3, 4, 0, 44 );
  R3( block, work, 0, 1, 2, 3, 4, 45 );
  R3( block, work, 4, 0, 1, 2, 3, 46 );
  R3( block, work, 3, 4, 0, 1, 2, 47 );
  R3( block, work, 2, 3, 4, 0, 1, 48 );
  R3( block, work, 1, 2, 3, 4, 0, 49 );
  R3( block, work, 0, 1, 2, 3, 4, 50 );
  R3( block, work, 4, 0, 1, 2, 3, 51 );
  R3( block, work, 3, 4, 0, 1, 2, 52 );
  R3( block, work, 2, 3, 4, 0, 1, 53 );
  R3( block, work, 1, 2, 3, 4, 0, 54 );
  R3( block, work, 0, 1, 2, 3, 4, 55 );
  R3( block, work, 4, 0, 1, 2, 3, 56 );
  R3( block, work, 3, 4, 0, 1, 2, 57 );
  R3( block, work, 2, 3, 4, 0, 1, 58 );
  R3( block, work, 1, 2, 3, 4, 0, 59 );
  
  /* Round 4 */
  R4( block, work, 0, 1, 2, 3, 4, 60 );
  R4( block, work, 4, 0, 1, 2, 3, 61 );
  R4( block, work, 3, 4, 0, 1, 2, 62 );
  R4( block, work, 2, 3, 4, 0, 1, 63 );
  R4( block, work, 1, 2, 3, 4, 0, 64 );
  R4( block, work, 0, 1, 2, 3, 4, 65 );
  R4( block, work, 4, 0, 1, 2, 3, 66 );
  R4( block, work, 3, 4, 0, 1, 2, 67 );
  R4( block, work, 2, 3, 4, 0, 1, 68 );
  R4( block, work, 1, 2, 3, 4, 0, 69 );
  R4( block, work, 0, 1, 2, 3, 4, 70 );
  R4( block, work, 4, 0, 1, 2, 3, 71 );
  R4( block, work, 3, 4, 0, 1, 2, 72 );
  R4( block, work, 2, 3, 4, 0, 1, 73 );
  R4( block, work, 1, 2, 3, 4, 0, 74 );
  R4( block, work, 0, 1, 2, 3, 4, 75 );
  R4( block, work, 4, 0, 1, 2, 3, 76 );
  R4( block, work, 3, 4, 0, 1, 2, 77 );
  R4( block, work, 2, 3, 4, 0, 1, 78 );
  R4( block, work, 1, 2, 3, 4, 0, 79 );
  
  /* Add the working vars back into context.iv[] */
  iv[0] += work[0];
  iv[1] += work[1];
  iv[2] += work[2];
  iv[3] += work[3];
  iv[4] += work[4];

} /* sechshuuid_sha1_transform() */


/*===========================================================================

FUNCTION SECHSHUUID_SHA1_INIT()

DESCRIPTION
  Initialize the hash context to prepare for message digest operations.

DEPENDENCIES
  None

RETURN VALUE
  None

SIDE EFFECTS
  None.
===========================================================================*/
void sechshuuid_sha1_init
(
  struct __sechshuuid_ctx_s* context             /* context */
)
{
  /* SHA1 initialization constants */
  context->iv[0] = 0x67452301;
  context->iv[1] = 0xefcdab89;
  context->iv[2] = 0x98badcfe;
  context->iv[3] = 0x10325476;
  context->iv[4] = 0xc3d2e1f0;
  
  context->counter[0] = context->counter[1] = 0;

} /* sechshuuid_sha1_init() */


/*===========================================================================

FUNCTION SECHSHUUID_SHA1_UPDATE()

DESCRIPTION
  Continue the SHA1 digest operation, process input in block-sized chunks,
  save the leftover input, update the context.

DEPENDENCIES
  None

RETURN VALUE
  None

SIDE EFFECTS
  None.
===========================================================================*/
void sechshuuid_sha1_update
(
  struct __sechshuuid_ctx_s* context,         /* context */
  UINT8*                     leftover,        /* leftover input in context */
  UINT16*                    leftover_size,
  UINT8*                     data, 
  UINT16                     len
)
{
  int i, j;
  /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  
  /* Compute number of bytes mod 64 */
  j = (context->counter[0] >> 3) & 63;
  
  /* Update number of bits */
  context->counter[0] += (len << 3);
  if ( context->counter[0] < (UINT16)(len << 3) )
  {
    context->counter[1]++;
  }
  context->counter[1] += (len >> 29);
  
  /* Transform as many times as possible. */
  if ( (j + len) > SECAPI_HSH_SHA_BLOCK_BYTE_LEN-1 )
  {
    i = SECAPI_HSH_SHA_BLOCK_BYTE_LEN - j;

    memcpy( &leftover[j], data, i );
    
    j = 0;
    
    sechshuuid_sha1_transform( context->iv, /*context->*/leftover );
    
    while (i + SECAPI_HSH_SHA_BLOCK_BYTE_LEN-1 < len)
    {
      sechshuuid_sha1_transform( context->iv, &data[i] );
      i += SECAPI_HSH_SHA_BLOCK_BYTE_LEN;
    }
  }
  else
  {
    i = 0;
  }
  
  /* Buffer remaining input */
  memcpy( &leftover[j], &data[i], (len - i) );

  /* Update context var for fallback */
  *leftover_size = (UINT16)(j + len - i);
} /* sechshuuid_sha1_update() */


/*===========================================================================

FUNCTION SECHSHUUID_SHA1_FINAL()

DESCRIPTION
  Do the final steps of a SHA1 hash, output the digest value, 
  clear the context.

DEPENDENCIES
  None

RETURN VALUE
  None

SIDE EFFECTS
  None.
===========================================================================*/
void sechshuuid_sha1_final
(
  struct __sechshuuid_ctx_s* context,         /* context */
  UINT8*                     leftover,        /* leftover input in context */
  UINT16*                    leftover_size,
  UINT8*                     digest
)
{
  unsigned long i;
  unsigned char finalcount[8];
  /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  
  for ( i = 0; i < 8; i++ )
  {
    /* Note: This is endianess-independent */
    finalcount[i] = (unsigned char) ((context->counter[(i >= 4 ? 0 : 1)]
                                      >> ((3-(i & 3)) * 8) ) & 255);
  }
  
  {
    unsigned char data[] = { 0x80};

    sechshuuid_sha1_update( context, leftover, leftover_size, data, sizeof( data ) );
  }
  
  while ( (context->counter[0] & 0x01f8) != 0x01c0 )
  {
    unsigned char data[] = { 0x00};
    
    sechshuuid_sha1_update( context, leftover, leftover_size, data, sizeof( data ) );
  }
  
  sechshuuid_sha1_update( context, leftover, leftover_size, finalcount, 8 );
  
  for ( i = 0; i < 20; i++ )
  {
    digest[i] = (unsigned char)
      ((context->iv[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
  }
} /* sechshuuid_sha1_final() */

